package com.google.code.gronono.commons.io.file;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.MessageFormat;

import com.google.code.gronono.commons.i18n.BundleKey;
import com.google.code.gronono.commons.i18n.BundleName;


/**
 * Classe utilitaire contenant des méthodes sur la manipulation de fichiers.
 *
 * @author Arnaud BRUNET
 */
@BundleName("com.google.code.gronono.commons.io")
public class FileUtils extends org.apache.commons.io.FileUtils {
	/** Message d'erreur lorsqu'un dossier n'existe pas. */
	@BundleKey("error.dir.not.exists")
	private static String ERROR_DIR_NOT_EXISTS = null;
	/** Message d'erreur lorsqu'un fichier n'est pas un dossier. */
	@BundleKey("error.file.is.not.directory")
	private static String ERROR_FILE_IS_NOT_DIRECTORY = null;
	/** Message d'erreur lorsqu'un dossier ne peut pas être créé. */
	@BundleKey("error.dir.cannot.be.created")
	private static String ERROR_DIR_CANNOT_BE_CREATED = null;
	/** Message d'erreur lorsqu'un dossier ne peut pas être lu. */
	@BundleKey("error.cannot.read.dir")
	private static String ERROR_CANNOT_READ_DIR = null;
	/** Message d'erreur lorsqu'un dossier ne peut pas être lu. */
	@BundleKey("error.cannot.write.dir")
	private static String ERROR_CANNOT_WRITE_DIR = null;
	/** Message d'erreur lorsqu'un fichier n'existe pas. */
	@BundleKey("error.file.not.exists")
	private static String ERROR_FILE_NOT_EXISTS = null;
	/** Message d'erreur lorsqu'un fichier n'est pas un fichier. */
	@BundleKey("error.file.is.not.file")
	private static String ERROR_FILE_IS_NOT_FILE = null;
	/** Message d'erreur lorsqu'un fichier ne peut pas être lu. */
	@BundleKey("error.cannot.read.file")
	private static String ERROR_CANNOT_READ_FILE = null;

	/**
	 * Cette classe est une classe utilitaire.
	 * Son constructeur ne doit jamais être appelé.
	 * @throws UnsupportedOperationException Toujours.
	 */
	private FileUtils() {
		throw new UnsupportedOperationException();
	}

	/**
	 * Vérifie qu'un dossier peut être lu.
	 * @param dir Le dossier à tester.
	 * @throws NullPointerException Si <code>dir</code> est <code>null</code>.
	 * @throws IOException Dans les cas suivants :
	 * 	<ul>
	 * 		<li>Si <code>dir</code> n'existe pas,</li>
	 * 		<li>Si <code>dir</code> n'est pas un dossier,</li>
	 * 		<li>Si <code>dir</code> n'est pas accessible en lecture.</li>
	 * 	</ul>
	 */
	public static void checkCanReadDir(final File dir) throws IOException {
		final String dirPath = dir.getAbsolutePath();
		if (!dir.exists()) {
			throw new FileNotFoundException(MessageFormat.format(FileUtils.ERROR_DIR_NOT_EXISTS, dirPath));
		}
		if (!dir.isDirectory()) {
			throw new IOException(MessageFormat.format(FileUtils.ERROR_FILE_IS_NOT_DIRECTORY, dirPath));
		}
		if (!dir.canRead()) {
			throw new IOException(MessageFormat.format(FileUtils.ERROR_CANNOT_READ_DIR, dirPath));
		}
	}

	/**
	 * Vérifie que l'utilisateur a le droit d'écrire dans un dossier.
	 * @param dir Le dossier à tester.
	 * @param create Indique si le dossier doit être créé s'il n'existe pas.
	 * @throws NullPointerException Si <code>dir</code> est <code>null</code>.
	 * @throws IOException Dans les cas suivants :
	 * 	<ul>
	 * 		<li>Si <code>dir</code> n'existe pas,</li>
	 * 		<li>Si <code>dir</code> n'est pas un dossier,</li>
	 * 		<li>Si <code>dir</code> n'est pas accessible en écriture.</li>
	 * 	</ul>
	 */
	public static void checkCanWriteDir(final File dir, final boolean create) throws IOException {
		if (dir == null) throw new FileNotFoundException(MessageFormat.format(FileUtils.ERROR_DIR_NOT_EXISTS, dir));
		final String dirPath = dir.getAbsolutePath();
		if (!dir.exists()) {
			if (create) {
				if (!dir.mkdirs()) {
					throw new IOException(MessageFormat.format(FileUtils.ERROR_DIR_CANNOT_BE_CREATED, dirPath));
				}
			} else {
				throw new FileNotFoundException(MessageFormat.format(FileUtils.ERROR_DIR_NOT_EXISTS, dirPath));
			}
		}
		if (!dir.isDirectory()) {
			throw new IOException(MessageFormat.format(FileUtils.ERROR_FILE_IS_NOT_DIRECTORY, dirPath));
		}
		if (!dir.canWrite()) {
			throw new IOException(MessageFormat.format(FileUtils.ERROR_CANNOT_WRITE_DIR, dirPath));
		}
	}

	/**
	 * Vérifie qu'un fichier peut être lu.
	 * @param file Le fichier à tester.
	 * @throws NullPointerException Si <code>file</code> est <code>null</code>.
	 * @throws IOException Dans les cas suivants :
	 * 	<ul>
	 * 		<li>Si <code>file</code> n'existe pas,</li>
	 * 		<li>Si <code>file</code> n'est pas un fichier,</li>
	 * 		<li>Si <code>file</code> n'est pas accessible en lecture.</li>
	 * 	</ul>
	 */
	public static void checkCanReadFile(final File file) throws IOException {
		if ((file == null) || !file.exists()) {
			throw new FileNotFoundException(MessageFormat.format(FileUtils.ERROR_FILE_NOT_EXISTS, file));
		}
		final String filePath = file.getAbsolutePath();
		if (!file.isFile()) {
			throw new IOException(MessageFormat.format(FileUtils.ERROR_FILE_IS_NOT_FILE, filePath));
		}
		if (!file.canRead()) {
			throw new IOException(MessageFormat.format(FileUtils.ERROR_CANNOT_READ_FILE, filePath));
		}
	}

	/**
	 * Retourne le prochain dossier parent valide.
	 *
	 * @param file Le fichier de départ.
	 * @return Le dossier parent s'il existe, <code>null</code> sinon.
	 */
	public static File getHighestExistingParentFolder(final File file) {
		File dir = file.getParentFile();
		if ((dir != null) && dir.exists() && dir.isDirectory()) {
			return dir;
		}
		while ((dir != null) && (!dir.exists() || !dir.isDirectory())) {
			dir = dir.getParentFile();
		}
		return dir;
	}
}
